$mac!(install);
$mac!(locate_project);
$mac!(login);
+ $mac!(metadata);
$mac!(new);
$mac!(owner);
$mac!(package);
--- /dev/null
+extern crate cargo;
+extern crate docopt;
+extern crate rustc_serialize;
+extern crate toml;
+
+use std::path::PathBuf;
+
+use cargo::ops::{output_metadata, OutputTo, OutputMetadataOptions};
+use cargo::util::important_paths::find_root_manifest_for_wd;
+use cargo::util::{CliResult, CliError, Config};
+
+#[derive(RustcDecodable)]
+struct Options {
+ flag_features: Vec<String>,
+ flag_manifest_path: Option<String>,
+ flag_no_default_features: bool,
+ flag_output_format: String,
+ flag_output_path: Option<String>,
+ flag_verbose: bool,
+ flag_quiet: bool,
+ flag_color: Option<String>,
+}
+
+pub const USAGE: &'static str = "
+Output the resolved dependencies of a project, the concrete used versions
+including overrides, in machine-readable format.
+
+Usage:
+ cargo metadata [options]
+
+Options:
+ -h, --help Print this message
+ -o, --output-path PATH Path the output is written to, otherwise stdout is used
+ -f, --output-format FMT Output format [default: toml]
+ Valid values: toml, json
+ --features FEATURES Space-separated list of features
+ --no-default-features Do not include the `default` feature
+ --manifest-path PATH Path to the manifest
+ -v, --verbose Use verbose output
+ -q, --quiet No output printed to stdout
+ --color WHEN Coloring: auto, always, never
+";
+
+pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
+ try!(config.shell().set_verbosity(options.flag_verbose, options.flag_quiet));
+ try!(config.shell().set_color_config(options.flag_color.as_ref().map(|s| &s[..])));
+ let manifest = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd()));
+
+ let output_to = match options.flag_output_path {
+ Some(path) => OutputTo::File(PathBuf::from(path)),
+ None => OutputTo::StdOut
+ };
+
+ let options = OutputMetadataOptions {
+ features: options.flag_features,
+ manifest_path: &manifest,
+ no_default_features: options.flag_no_default_features,
+ output_format: options.flag_output_format,
+ output_to: output_to,
+ };
+
+ output_metadata(options, config)
+ .map(|_| None)
+ .map_err(|err| CliError::from_boxed(err, 101))
+}
--- /dev/null
+use std::ascii::AsciiExt;
+use std::collections::HashMap;
+use std::path::{Path, PathBuf};
+
+use core::resolver::Resolve;
+use core::{Source, Package};
+use ops;
+use rustc_serialize::json;
+use sources::PathSource;
+use toml;
+use util::config::Config;
+use util::{paths, CargoResult};
+
+
+/// Where the dependencies should be written to.
+pub enum OutputTo {
+ File(PathBuf),
+ StdOut,
+}
+
+pub struct OutputMetadataOptions<'a> {
+ pub features: Vec<String>,
+ pub output_format: String,
+ pub output_to: OutputTo,
+ pub manifest_path: &'a Path,
+ pub no_default_features: bool,
+}
+
+/// Loads the manifest, resolves the dependencies of the project to the concrete
+/// used versions - considering overrides - and writes all dependencies in a TOML
+/// format to stdout or the specified file.
+///
+/// The TOML format is e.g.:
+/// ```toml
+/// root = "libA"
+///
+/// [packages.libA]
+/// dependencies = ["libB"]
+/// path = "/home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/libA-0.1"
+/// version = "0.1"
+///
+/// [packages.libB]
+/// dependencies = []
+/// path = "/home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/libB-0.4"
+/// version = "0.4"
+///
+/// [packages.libB.features]
+/// featureA = ["featureB"]
+/// featureB = []
+/// ```
+pub fn output_metadata(opt: OutputMetadataOptions, config: &Config) -> CargoResult<()> {
+ let deps = try!(resolve_dependencies(opt.manifest_path,
+ config,
+ opt.features,
+ opt.no_default_features));
+ let (resolved_deps, packages) = deps;
+
+ #[derive(RustcEncodable)]
+ struct RootPackageInfo<'a> {
+ name: &'a str,
+ version: String,
+ features: Option<&'a HashMap<String, Vec<String>>>,
+ }
+
+ #[derive(RustcEncodable)]
+ struct ExportInfo<'a> {
+ root: RootPackageInfo<'a>,
+ packages: Vec<&'a Package>,
+ }
+
+ let mut output = ExportInfo {
+ root: RootPackageInfo {
+ name: resolved_deps.root().name(),
+ version: format!("{}", resolved_deps.root().version()),
+ features: None,
+ },
+ packages: Vec::new(),
+ };
+
+ for package in packages.iter() {
+ output.packages.push(&package);
+ if package.package_id() == resolved_deps.root() {
+ let features = package.manifest().summary().features();
+ if !features.is_empty() {
+ output.root.features = Some(features);
+ }
+ }
+ }
+
+ let serialized_str = match &opt.output_format.to_ascii_uppercase()[..] {
+ "TOML" => toml::encode_str(&output),
+ "JSON" => try!(json::encode(&output)),
+ _ => bail!("unknown format: {}, supported formats are TOML, JSON.",
+ opt.output_format),
+ };
+
+ match opt.output_to {
+ OutputTo::StdOut => println!("{}", serialized_str),
+ OutputTo::File(ref path) => try!(paths::write(path, serialized_str.as_bytes()))
+ }
+
+ Ok(())
+}
+
+/// Loads the manifest and resolves the dependencies of the project to the
+/// concrete used versions. Afterwards available overrides of dependencies are applied.
+fn resolve_dependencies(manifest: &Path,
+ config: &Config,
+ features: Vec<String>,
+ no_default_features: bool)
+ -> CargoResult<(Resolve, Vec<Package>)> {
+ let mut source = try!(PathSource::for_path(manifest.parent().unwrap(), config));
+ try!(source.update());
+
+ let package = try!(source.root_package());
+
+ let deps = try!(ops::resolve_dependencies(&package,
+ config,
+ Some(Box::new(source)),
+ features,
+ no_default_features));
+
+ let (packages, resolve_with_overrides, _) = deps;
+
+ Ok((resolve_with_overrides, packages))
+}
pub use self::cargo_fetch::{fetch, get_resolved_packages};
pub use self::cargo_pkgid::pkgid;
pub use self::resolve::{resolve_pkg, resolve_with_previous};
+pub use self::cargo_output_metadata::{output_metadata, OutputTo, OutputMetadataOptions};
mod cargo_clean;
mod cargo_compile;
mod cargo_generate_lockfile;
mod cargo_install;
mod cargo_new;
+mod cargo_output_metadata;
mod cargo_package;
mod cargo_pkgid;
mod cargo_read_manifest;
--- /dev/null
+use std::fs::File;
+use std::io::prelude::*;
+
+use hamcrest::{assert_that, existing_file, is, equal_to};
+use support::{project, execs, basic_bin_manifest};
+
+
+fn setup() {
+}
+
+test!(cargo_metadata_simple {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"));
+
+ assert_that(p.cargo_process("metadata"), execs().with_stdout(r#"
+[[packages]]
+dependencies = []
+id = "foo 0.5.0 [..]"
+manifest_path = "[..]Cargo.toml"
+name = "foo"
+version = "0.5.0"
+
+[packages.features]
+
+[[packages.targets]]
+kind = ["bin"]
+name = "foo"
+src_path = "src[..]foo.rs"
+
+[root]
+name = "foo"
+version = "0.5.0"
+
+"#));
+});
+
+
+test!(cargo_metadata_simple_json {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"));
+
+ assert_that(p.cargo_process("metadata").arg("-f").arg("json"), execs().with_stdout(r#"
+ {
+ "root": {
+ "name": "foo",
+ "version": "0.5.0",
+ "features": null
+ },
+ "packages": [
+ {
+ "name": "foo",
+ "version": "0.5.0",
+ "id": "foo[..]",
+ "source": null,
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "name": "foo",
+ "src_path": "src[..]foo.rs"
+ }
+ ],
+ "features": {},
+ "manifest_path": "[..]Cargo.toml"
+ }
+ ]
+ }"#.split_whitespace().collect::<String>()));
+});
+
+test!(cargo_metadata_with_invalid_manifest {
+ let p = project("foo")
+ .file("Cargo.toml", "");
+
+ assert_that(p.cargo_process("metadata"), execs().with_status(101)
+ .with_stderr("\
+failed to parse manifest at `[..]`
+
+Caused by:
+ no `package` or `project` section found."))
+});
+
+test!(cargo_metadata_with_invalid_output_format {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"));
+
+ assert_that(p.cargo_process("metadata").arg("--output-format").arg("XML"),
+ execs().with_status(101)
+ .with_stderr("unknown format: XML, supported formats are TOML, JSON."))
+});
+
+test!(cargo_metadata_simple_file {
+ let p = project("foo")
+ .file("Cargo.toml", &basic_bin_manifest("foo"));
+
+ assert_that(p.cargo_process("metadata").arg("--output-path").arg("metadata.toml"),
+ execs().with_status(0));
+
+ let outputfile = p.root().join("metadata.toml");
+ assert_that(&outputfile, existing_file());
+
+ let mut output = String::new();
+ File::open(&outputfile).unwrap().read_to_string(&mut output).unwrap();
+
+ assert_that(output[..].contains(r#"name = "foo""#), is(equal_to(true)));
+});
mod test_cargo_generate_lockfile;
mod test_cargo_init;
mod test_cargo_install;
+mod test_cargo_metadata;
mod test_cargo_new;
mod test_cargo_package;
mod test_cargo_profiles;